// Copyright (C) Calum Grant 2008

#include "cppscript"
#include "dynamic/extensions.hpp"

static_call( register_pickle_type( cmp_reverse, reverse(null) ) );
static_call( register_pickle_type( cmp_transform, transform(null,null) ) );
static_call( register_pickle_type( cmp_filter, filter(null, null) ) );
static_call( register_pickle_type( cmp_tail, tail(null) ) );

using internal::nonroot_var;


namespace dynamic
{
	namespace types
	{
		/// Implements a reverse container, obtained by the \ref reverse() function.
		class reverse_impl : public dynamic::shared_var_impl
		{
			nonroot_var m_container;

		public:
			reverse_impl(const var & container) : m_container(container) 
			{ 
			}

			void mark_children(gc::garbage_collector &gc)
			{
				m_container.ref().impl().mark_reachable(gc);
			}

			var_cmp_index comparison_index()
			{ 
				return cmp_reverse; 
			}

			var begin() 
			{ 
				return m_container.ref().rbegin(); 
			}

			var end() 
			{ 
				return m_container.ref().rend();
			}

			var rbegin() 
			{ 
				return m_container.ref().begin(); 
			}

			var rend()
			{ 
				return m_container.ref().end(); 
			}

			int size() 
			{ 
				return m_container.ref().size(); 
			}

			var back() 
			{ 
				return m_container.ref().front();
			}

			var front() 
			{ 
				return m_container.ref().back(); 
			}

			var pop_front() 
			{ 
				return m_container.ref().pop_back(); 
			}

			var pop_back() 
			{
				return m_container.ref().pop_front();
			}

			void push_back(const var & v) 
			{
				m_container.ref().push_front(v);
			}

			void push_front(const var & v)
			{
				m_container.ref().push_back(v);
			}

			var enumerator() 
			{ 
				return m_container.ref().reverse_enumerator();
			}

			var reverse_enumerator()
			{
				return m_container.ref().enumerator();
			}

			bool empty() 
			{ 
				return m_container.ref().empty(); 
			}

			std::string class_name() 
			{ 
				return "reverse"; 
			}

			void output(ostream & os)
			{
				os << "reverse(";
				m_container.ref().impl().output(os); 
				os << ")";
			}

			void output(wostream & os)
			{
				os << L"reverse(";
				m_container.ref().impl().output(os); 
				os << L")";
			}

			void pickle(pickler & p)
			{
				p.write_object_type(cmp_reverse);
				m_container.ref().impl().pickle(p);
			}

			void unpickle(unpickler & p)
			{
				m_container = p.read_object();
			}

			bool as_bool() 
			{ 
				return m_container.ref();
			}

			var keys() 
			{ 
				return reverse(m_container.ref().keys());
			}

			var values() 
			{ 
				return reverse(m_container.ref().values());
			}

			var get_member(const char * s) 
			{ 
				return m_container.ref()[s]; 
			}

			var get_member(int i)
			{ 
				return m_container.ref()[i];
			}

			var get_member(const var & v)
			{ 
				return m_container.ref()[v];
			}

			void clear()
			{ 
				m_container.ref().clear();
			}

			bool contains(const var & v)
			{ 
				return m_container.ref().contains(v); 
			}

			void erase(const var & v)
			{ 
				m_container.ref().erase(v);
			}

			var clone()
			{ 
				return new reverse_impl(*this); 
			}
		};


		/// Implements a filter container, returned by the filter() function.
		class filter_impl : public dynamic::shared_var_impl
		{
			nonroot_var m_container, m_predicate;
		public:
			filter_impl(const var & c, const var & p) : m_container(c), m_predicate(p) 
			{ 
			}

			std::string class_name() 
			{ 
				return "filter";
			}

			var_cmp_index comparison_index() 
			{ 
				return cmp_filter; 
			}

			void mark_children(gc::garbage_collector &gc)
			{
				m_container.ref().impl().mark_reachable(gc);
				m_predicate.ref().impl().mark_reachable(gc);
			}

			void output(ostream & os)
			{
				os << "filter(";
				m_container.ref().impl().output(os);
				os << ")";
			}

			void output(wostream & os)
			{
				os << L"filter(";
				m_container.ref().impl().output(os);
				os << L")";
			}

			void pickle(pickler & p)
			{
				p.write_object_type(cmp_filter);
				p.write_object(m_container.ref());
				p.write_object(m_predicate.ref());
			}

			void unpickle(unpickler & p)
			{
				m_container = p.read_object();
				m_predicate = p.read_object();
			}

			class iterator : public shared_var_impl
			{
				nonroot_var m_enumerator, m_pred;
			public:
				iterator(const var & enumerator, nonroot_var & p) : 
					m_enumerator(enumerator), m_pred(p) 
				{ 
					while( m_enumerator.ref() && !m_pred.ref()(*m_enumerator.ref()) )
						++m_enumerator.ref(); 
				}

				bool as_bool() 
				{ 
					return m_enumerator.ref().as_bool(); 
				}

				std::string class_name() 
				{ 
					return "filter_enumerator"; 
				}

				void op_inc() 
				{ 
					do
					{
						++m_enumerator.ref(); 
					}
					while( m_enumerator.ref() && !m_pred.ref()(*m_enumerator.ref()) );
				}

				var deref() 
				{ 
					return *m_enumerator.ref();
				}

				void mark_children(gc::garbage_collector &gc)
				{
					m_enumerator.ref().impl().mark_reachable(gc);
					m_pred.ref().impl().mark_reachable(gc);
				}
			};

			var enumerator() 
			{ 
				return new iterator(m_container.ref().enumerator(), m_predicate);
			}

			var reverse_enumerator()
			{ 
				return new iterator(m_container.ref().reverse_enumerator(), m_predicate);
			}

			var clone() 
			{ 
				return new filter_impl(*this); 
			}
		};


		/// Implements a transformation container, returned by \ref transform().
		class transform_impl : public dynamic::shared_var_impl
		{
			nonroot_var m_container, m_fn;

		public:
			transform_impl(const var & c, const var & f) : m_container(c), m_fn(f) 
			{ 
			}

			std::string class_name() 
			{ 
				return "transform"; 
			}

			void mark_children(gc::garbage_collector &gc)
			{
				m_container.ref().impl().mark_reachable(gc);
				m_fn.ref().impl().mark_reachable(gc);
			}

			var_cmp_index comparison_index() 
			{ 
				return cmp_transform; 
			}

			void output(ostream & os)
			{
				os << "transform(";
				m_container.ref().impl().output(os);
				os << ")";
			}

			void output(wostream & os)
			{
				os << L"transform(";
				m_container.ref().impl().output(os);
				os << L")";
			}

			void pickle(pickler & p)
			{
				p.write_object_type(cmp_transform);
				p.write_object(m_container.ref());
				p.write_object(m_fn.ref());
			}

			void unpickle(unpickler & p)
			{
				m_container = p.read_object();
				m_fn = p.read_object();
			}

			int size() 
			{ 
				return m_container.ref().size(); 
			}

			bool as_bool() 
			{ 
				return m_container.ref(); 
			}

			bool empty() 
			{ 
				return m_container.ref().empty(); 
			}

			class iterator : public shared_var_impl
			{
				nonroot_var m_enumerator, m_fn;

			public:
				iterator(const var & enumerator, nonroot_var & f) : 
					m_enumerator(enumerator), m_fn(f) 
				{ 
				}

				bool as_bool() 
				{ 
					return m_enumerator.ref().as_bool(); 
				}

				std::string class_name() 
				{ 
					return "transform_enumerator"; 
				}

				void op_inc() 
				{ 
					++m_enumerator.ref(); 
				}

				var deref() 
				{ 
					return m_fn.ref()(*m_enumerator.ref()); 
				}

				void mark_children(gc::garbage_collector &gc)
				{
					m_enumerator.ref().impl().mark_reachable(gc);
					m_fn.ref().impl().mark_reachable(gc);
				}
			};

			var enumerator()
			{ 
				return new iterator( m_container.ref().enumerator(), m_fn ); 
			}

			var reverse_enumerator()
			{ 
				return new iterator( m_container.ref().reverse_enumerator(), m_fn ); 
			}

			var clone() 
			{ 
				return new transform_impl(*this); 
			}

			var front() 
			{ 
				return m_fn.ref()(m_container.ref().front()); 
			}

			var back() 
			{ 
				return m_fn.ref()(m_container.ref().back()); 
			}
		};


		/// Implements a tail container, returned by the tail() function.
		class tail_impl : public dynamic::shared_var_impl
		{
			nonroot_var m_container;

		public:
			tail_impl(const var & c) : m_container(c)
			{ 
			}

			std::string class_name() 
			{ 
				return "tail"; 
			}

			void mark_children(gc::garbage_collector &gc)
			{
				m_container.ref().impl().mark_reachable(gc);
			}

			var_cmp_index comparison_index() 
			{ 
				return cmp_tail; 
			}

			void output(ostream & os)
			{
				os << "tail(";
				m_container.ref().impl().output(os);
				os << ")";
			}

			void output(wostream & os)
			{
				os << L"tail(";
				m_container.ref().impl().output(os);
				os << L")";
			}

			void pickle(pickler & p)
			{
				p.write_object_type(cmp_tail);
				p.write_object(m_container.ref());
			}

			void unpickle(unpickler & p)
			{
				m_container = p.read_object();
			}

			int size() 
			{ 
				int rs = m_container.ref().size()-1;
				return rs>0 ? rs : 0;
			}

			bool as_bool() 
			{ 
				return size()>0; 
			}

			bool empty() 
			{ 
				return size()==0; 
			}

			var enumerator()
			{
				return ++m_container.ref().enumerator();
			}

			var begin() 
			{ 
				return enumerator(); 
			}

			var end() 
			{ 
				return m_container.ref().end(); 
			}

			var front() 
			{ 
				return *enumerator(); 
			}

			var back() 
			{ 
				return m_container.ref().back(); 
			}

			// reverse isn't supported.

			var clone() 
			{ 
				return new tail_impl(*this); 
			}
		};


		/// Implements a enumeration enumerator.
		class enumerate_enumerator : public dynamic::shared_var_impl
		{
			nonroot_var m_fn, m_current_value;

		public:
			enumerate_enumerator(nonroot_var & fn) : 
				m_fn(fn), 
				m_current_value( m_fn.ref()() ) 
			{
			}

			void mark_children(gc::garbage_collector & gc)
			{
				m_fn.ref().impl().mark_reachable(gc);
				m_current_value.ref().impl().mark_reachable(gc);
			}

			bool as_bool() 
			{ 
				return m_current_value.ref() != null; 
			}

			var deref() 
			{ 
				return m_current_value.get(); 
			}

			void op_inc() 
			{ 
				m_current_value = m_fn.ref()(); 
			}

			std::string class_name() 
			{ 
				return "fn_enumerator2"; 
			}
		};


		/// Implements an enumeration container, returned by the \ref enumerate() function.
		class enumerate_impl : public dynamic::shared_var_impl
		{
			nonroot_var m_fn;
		public:

			enumerate_impl(const var & fn) : m_fn(fn) 
			{ 
			}

			void mark_children(gc::garbage_collector & gc)
			{
				m_fn.ref().impl().mark_reachable(gc);
			}

			std::string class_name() 
			{ 
				return "fn_enumerator"; 
			}

			var enumerator()
			{
				return new enumerate_enumerator(m_fn);
			}
		};
	}
}


var dynamic::reverse(const var & container)
{
	return new types::reverse_impl(container);
}


var dynamic::filter(const var & container, const var & pred)
{
	return new types::filter_impl(container, pred);
}


var dynamic::transform(const var & container, const var & fn)
{
	return new types::transform_impl(container, fn);
}


var dynamic::tail(const var & container)
{
	return new types::tail_impl(container);
}


var dynamic::enumerate(const var & fn)
{
	return new types::enumerate_impl(fn);
}
